precision highp float;

//glsllib
#import "Common/ShaderLib/Lighting.glsllib"
uniform vec4 g_LightData[NB_LIGHTS];
#define PI          3.14159265359f
#define invPI       0.3183098861837697f
#define invTWO_PI   0.15915494309f
#define saturate(x) clamp(x, 0.0f, 1.0f)
#define NORMALMAP 1
#define IRRADIANCEMAP 1
#define RADIANCEMAP 1
const vec2 invAtan = vec2(0.1591f, 0.3183f);

vec3 Diffuse_Lambert( vec3 DiffuseColor )
{
    return DiffuseColor * (1.0f / PI);
}

float D_GGX( float Roughness, float NoH )
{
    float m = Roughness * Roughness;
    float m2 = m * m;
    float d = ( NoH * m2 - NoH ) * NoH + 1.0f;    // 2 mad
    return m2 / ( PI*d*d );                     // 4 mul, 1 rcp
}

float Vis_Smith( float Roughness, float NoV, float NoL )
{
    float a = Roughness * Roughness ;
    float a2 = a*a;

    float Vis_SmithV = NoV + sqrt( NoV * (NoV - NoV * a2) + a2 );
    float Vis_SmithL = NoL + sqrt( NoL * (NoL - NoL * a2) + a2 );
    return 1.0f / ( Vis_SmithV * Vis_SmithL );
}

vec3 F_Schlick( vec3 SpecularColor, float VoH )
{
    float Fc = pow( 1.0f - VoH, 5.0f );
    return Fc + (1.0f - Fc) * SpecularColor;
}

float getAttenuation( vec3 lightPosition, vec3 fragPos, float lightRadius )
{
    float r                = lightRadius;
    vec3 L                 = lightPosition - fragPos;
    float dist             = length(L);
    float d                = max( dist - r, 0.0f );
    L                      /= dist;
    float denom            = d / r + 1.0f;
    float attenuation      = 1.0f / (denom*denom);
    float cutoff           = 0.0052f;
    attenuation            = (attenuation - cutoff) / (1.0f - cutoff);
    attenuation            = max(attenuation, 0.0f);
    
    return attenuation;
}
vec2 getuv(vec3 p){
    float theta = acos(p.y);
    float phi = atan(p.z, p.x);
    if (phi < 0.0f) {
            phi += 2.0f * PI;
    }
    vec2 s;
    s.x = 1.0f - phi * invTWO_PI;
    s.y = theta * invPI;
    return s;
}


vec2 getuv2(vec3 v)
{
    vec2 uv = vec2(atan(v.z, v.x), asin(v.y));
    uv *= invAtan;
    uv += 0.5f;
    return uv;
}

vec3 EnvBRDFApprox( vec3 SpecularColor, float Roughness, float NoV )
{
	const vec4 c0 = vec4( -1.0f, -0.0275f, -0.572f, 0.022f );
	const vec4 c1 = vec4( 1.0f, 0.0425f, 1.04f, -0.04f );
	vec4 r = Roughness * c0 + c1;
	float a004 = min( r.x * r.x, exp2( -9.28f * NoV ) ) * r.x + r.y;
	vec2 AB = vec2( -1.04f, 1.04f ) * a004 + r.zw;
	AB.y *= saturate( 50.0f * SpecularColor.y );
	return SpecularColor * AB.x + AB.y;
}

vec4 lerpMip(vec3 lookup,float mip)
{
	vec2 rauv = getuv(lookup);
	rauv.y = rauv.y * 0.125f;
	float mip1 = floor(mip);
	float mip2 = mip1 + 1.0f;
	mip2 = clamp(mip2, 0.0f, 5.0f);
	vec2 mip1uv = vec2(rauv.x,rauv.y + mip1 * 0.125f);
	vec2 mip2uv = vec2(rauv.x,rauv.y + mip2 * 0.125f);
	return vec4(mip1uv.x,mip1uv.y,mip2uv.x,mip2uv.y);
}

vec3 getHdr(vec4 src)
{
    vec3 outc = vec3(1.0f,1.0f,1.0f);
    float e = src.w * 255.0f - 128.0f;
    e = pow(2.0f,e);
    outc.x = src.x * e;
    outc.y = src.y * e;
    outc.z = src.z * e;
    return outc;
}

//hdr tone mapping
vec3 toneMap(vec3 src){
    vec3 color = src / (1.0f + src);
    color = pow(color,vec3(1.0f/2.2f,1.0f/2.2f,1.0f/2.2f));
    return color;
}

vec3 getNormalFromMap(sampler2D normalMap, vec2 TexCoords, mat3 TBN)
{
    vec3 tangentNormal = texture2D(normalMap, TexCoords).xyz * 2.0f - 1.0f;

    //vec3 Q1  = dFdx(WorldPos);
    //vec3 Q2  = dFdy(WorldPos);
    //vec2 st1 = dFdx(TexCoords);
    //vec2 st2 = dFdy(TexCoords);

    //vec3 N   = normalize(Normal);
    //vec3 T  = normalize(Q1*st2.t - Q2*st1.t);
    //vec3 B  = -normalize(cross(N, T));
    //mat3 TBN = mat3(T, B, N);

    return normalize(TBN * tangentNormal);
}

vec2 SampleSphericalMap(vec3 v)
{
    vec2 uv = vec2(atan(v.z, v.x), asin(v.y));
    uv *= invAtan;
    uv += 0.5f;
    return uv;
}

vec3 getNormap(vec3 t,vec3 n,vec3 color)
{
    vec3 b = cross(t,n);
    b = normalize(b);
    mat3 tbn = mat3(t,b,n);
    return normalize(tbn * color);
}
const float NORMAL_TYPE = -1.0f;
void main(){
    vec3 norm = normalize(iNormal);
    #if defined(NORMALMAP)
        vec3 tan = normalize(iTangent.xyz);
        mat3 tbnMat = mat3(tan, iTangent.w * cross( (norm), (tan)), norm);
        vec4 normalHeight = texture2D(uNormalMap, iTexCoords);
        vec3 normal = normalize((normalHeight.xyz * vec3(2.0f, NORMAL_TYPE * 2.0f, 2.0f) - vec3(1.0f, NORMAL_TYPE * 1.0f, 1.0f)));
        normal = normalize(tbnMat * normal);
    #else
        vec3 normal = norm;
    #endif
    vec3 N = normal;
    
    vec4 albeoColor = uAlbeoColor;
    vec3 baseColor  = albeoColor.rgb;
    float roughness = uRoughness;
    float ao = uAo;
    float metallic = uMetallic;
    vec3 V               = normalize( uCamPosition - iPosition );
    float NoV            = saturate( dot( N, V ) );
    vec3 diffuseColor    = baseColor - baseColor * metallic;
    vec3 specularColor   = mix( vec3( 0.08f * iSpecular ), baseColor, metallic );
    
    vec3 dLights = vec3(0.0f);
    for(int i = 0;i < NB_LIGHTS;i+=3){
        vec4 lightColor = g_LightData[i];            
        vec4 lightData1 = g_LightData[i+1];
        
        vec4 lightDir4;
        vec3 lightVec;
        lightComputeDir(iPosition, lightColor.w, lightData1, lightDir4, lightVec);
        
        //vec3 L = normalize(lightData1.xyz - iPosition);
        vec3 L               = normalize(lightDir4.xyz);
        vec3 H               = normalize(V + L);

        float NoL            = saturate( dot( N, L ) );
        
        float VoH            = saturate( dot( V, H ) );
        float NoH            = saturate( dot( N, H ) );

        

        float attenuation    = getAttenuation( lightData1.xyz, iPosition, 1.0f / lightData1.w );
        float D              = D_GGX(roughness,NoH);
        float G              = Vis_Smith(roughness,NoV,NoL); 
        vec3  F              = F_Schlick(specularColor,VoH);

        vec3 diffusePoint    = Diffuse_Lambert(diffuseColor);
        vec3 specularPoint   = D * G * F;
        dLights             += lightColor.rgb * ( diffusePoint + specularPoint ) * NoL * attenuation;
    }
    
    
    
    #if defined(IRRADIANCEMAP) && defined(RADIANCEMAP)
        vec3 color;
        float numMips	 = 5.0f;
        float mip		 = clamp(log2(roughness) + numMips,0.0f,numMips);
        float mixmip 	 = fract(mip);
        vec3 lookup		 = -reflect( V, N );

        vec4 mipuv 		 = lerpMip(lookup,mip);
        vec3 radiance1	 = getHdr(texture2D( uRadianceMap, mipuv.xy));
        vec3 radiance2	 = getHdr(texture2D( uRadianceMap, mipuv.zw));
        vec3 radiance 	 = mix(radiance1, radiance2,vec3(mixmip,mixmip,mixmip));
        vec3 irradiance	 = getHdr(texture2D( uIrradianceMap,SampleSphericalMap(iNormal) ));

        vec3 reflectance	 = EnvBRDFApprox( specularColor, roughness, NoV );

        vec3 diffuse  	 = diffuseColor * irradiance;
        vec3 specular 	 = radiance * reflectance;
        color		 = diffuse + specular + dLights;
    #else
        vec3 color       = dLights;
    #endif
    #ifdef TONEMAP
        color 		 = toneMap(color);
    #endif

    oColor = vec4(color * ao * albeoColor.w, albeoColor.w);

}